package com.activiti.workflow.utils;

import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.editor.language.json.converter.util.CollectionUtils;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.history.HistoryManager;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
import org.activiti.engine.impl.util.ProcessDefinitionUtil;
import org.activiti.engine.task.Task;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class ActivitiUtil {

    /**
     * 驳回 / 回退
     * 按照这种方法，可以回退至任意节点
     */
    public static void backProcess(ProcessEngine processEngine, Task task) {
        HistoryService historyService = processEngine.getHistoryService();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        TaskService taskService = processEngine.getTaskService();

        String processInstanceId = task.getProcessInstanceId();

        // 获取所有历史任务（按创建时间降序）
        List<HistoricTaskInstance> hisTaskList = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .desc()
                .list();

        // 获取当前流程实例的所有的历史活动实例
        List<HistoricActivityInstance> hisActivityList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).list();

        // 如果历史活动实例为空 或者 数量才1个 说明不存在或者才刚开启流程
        if (CollectionUtils.isEmpty(hisTaskList) || hisTaskList.size() < 2) {
            return;
        }

        // 当前任务（通过历史任务按时间倒序，查询第1个）
        HistoricTaskInstance currentTask = hisTaskList.get(0);
        // 前一个任务（通过历史任务按时间倒序，查询第2个）
        HistoricTaskInstance lastTask = hisTaskList.get(1);

        //  当前活动（遍历当前流程实例的所有的历史活动实例, 根据当前任务的id（就是taskId）与历史活动实例的taskId相等, 找到对应的历史活动实例）
        HistoricActivityInstance currentActivity = hisActivityList.stream().filter(e -> currentTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);
        //  前一个活动（遍历当前流程实例的所有的历史活动实例, 根据前一个任务的id（就是taskId）与历史活动实例的taskId相等, 找到对应的历史活动实例）
        HistoricActivityInstance lastActivity = hisActivityList.stream().filter(e -> lastTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);

        // 使用repositoryService, 根据流程定义id获取 【bpmn模型对象】
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());

        //  获取前一个活动节点（从bpmn模型对象中，根据活动实例的活动id（就是标签的id属性）找到FlowNode。所以活动实例其实就是把taskId和活动id给结合起来了）
        FlowNode lastFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(lastActivity.getActivityId());
        //  获取当前活动节点（从bpmn模型对象中，根据活动实例的活动id（就是标签的id属性）找到FlowNode。所以活动实例其实就是把taskId和活动id给结合起来了）
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivity.getActivityId());

        //  临时保存当前活动的原始方向（这说明每1个FlowNode都包含1个SequenceFlow的outgoingFlows集合）
        List<SequenceFlow> originalSequenceFlowList = new ArrayList<>(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();

        //  建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode); // 当前flowNode
        newSequenceFlow.setTargetFlowElement(lastFlowNode);    // 上一个flowNode
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        //  当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        taskService.addComment(task.getId(), task.getProcessInstanceId(), "部门经理意见未填写, 回退至上一节点");

        //  完成当前任务（将会沿着新给定的方向流转到指定的节点）
        taskService.complete(task.getId());

        //  重新查询当前任务
        Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (null != nextTask) {
            // 因为现在已经退回到上一个节点了, 这里从历史任务中获取上1个节点的任务负责人, 设置到当前任务中
            taskService.setAssignee(nextTask.getId(), lastTask.getAssignee());
        }
        //  恢复原始方向
        currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
    }
    /**
     * 跳到最开始的任务节点（直接打回）
     * @param task 当前任务
     */
    public static void jumpToStart(ProcessEngine processEngine,Task task) {
        HistoryService historyService = processEngine.getHistoryService();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        TaskService taskService = processEngine.getTaskService();

        String processInstanceId = task.getProcessInstanceId();

        //  获取所有历史任务（按创建时间升序）
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .asc()
                .list();

        if (CollectionUtils.isEmpty(hisTaskList) || hisTaskList.size() < 2) {
            return;
        }

        //  第一个任务
        HistoricTaskInstance startTask = hisTaskList.get(0);
        //  当前任务
        HistoricTaskInstance currentTask = hisTaskList.get(hisTaskList.size() - 1);

        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());

        //  获取第一个活动节点
        FlowNode startFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(startTask.getTaskDefinitionKey());
        //  获取当前活动节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentTask.getTaskDefinitionKey());

        //  临时保存当前活动的原始方向
        List<SequenceFlow> originalSequenceFlowList = new ArrayList<>(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();

        //  建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(startFlowNode);
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        //  当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        //  完成当前任务
        taskService.complete(task.getId());

        //  重新查询当前任务
        Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (null != nextTask) {
            taskService.setAssignee(nextTask.getId(), startTask.getAssignee());
        }

        //  恢复原始方向
        currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
    }

    /**
     * 结束任务
     * @param task  当前任务
     */
    public static void endTask(ProcessEngine processEngine,Task task) {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        TaskService taskService = processEngine.getTaskService();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        List<EndEvent> endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        FlowNode endFlowNode = endEventList.get(0);
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());

        //  临时保存当前活动的原始方向
        List<SequenceFlow> originalSequenceFlowList = new ArrayList<>(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();

        // 建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);

        // 当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        // 完成当前任务
        taskService.complete(task.getId());

        // 可以不用恢复原始方向，不影响其它的流程
        currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
    }

    public static void jumpAnyNode(ProcessEngine processEngine,String taskId,String targetNodeId) {
        processEngine.getManagementService().executeCommand(new JumpAnyWhereCmd(taskId, targetNodeId));
    }

    public static class JumpAnyWhereCmd implements Command<Object> {
        private String taskId;

        private String targetNodeId;

        public JumpAnyWhereCmd(String taskId, String targetNodeId) {
            this.taskId = taskId;
            this.targetNodeId = targetNodeId;
        }

        public Object execute(CommandContext commandContext) {
            //获取任务实例管理类
            TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager();
            //获取当前任务实例
            TaskEntity currentTask = taskEntityManager.findById(taskId);

            //获取当前节点的执行实例
            ExecutionEntity execution = currentTask.getExecution();
            String executionId = execution.getId();

            //获取流程定义id
            String processDefinitionId = execution.getProcessDefinitionId();
            //获取目标节点
            Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
            FlowElement flowElement = process.getFlowElement(targetNodeId);

            //获取历史管理
            HistoryManager historyManager = commandContext.getHistoryManager();

            //通知当前活动结束(更新act_hi_actinst)
            historyManager.recordActivityEnd(execution,"jump to userTask1");
            //通知任务节点结束(更新act_hi_taskinst)
            historyManager.recordTaskEnd(taskId,"jump to userTask1");
            //删除正在执行的当前任务
            taskEntityManager.delete(taskId);

            //此时设置执行实例的当前活动节点为目标节点
            execution.setCurrentFlowElement(flowElement);

            //向operations中压入继续流程的操作类
            commandContext.getAgenda().planContinueProcessOperation(execution);

            return null;
        }
    }
}
